﻿//*** Guidance generated code ***//
using System;
using System.Collections.Generic;
using System.Reflection;
using System.Linq;

using Newtonsoft.Json.Linq;
using Newtonsoft.Json.Serialization;

using ProjectBase.Core;

namespace $safeprojectname$
{
    /// <summary>
    /// Returns the ToDisplay() string, instead of the IPersistent reference
    /// </summary>
    public class PersistentValueProvider : IValueProvider
    {
        readonly PropertyInfo _persistentPropertyInfo;
        readonly string _propertyName;
        readonly Type _propertyType;
        readonly IPersistentObject _persistent;
        readonly IList<string> _allInnerProperties;

        public PersistentValueProvider(PropertyInfo persistentPropertyInfo, IList<string> allInnerProperties)
        {
            _persistentPropertyInfo = persistentPropertyInfo;
            _propertyName = _persistentPropertyInfo.Name;
            _propertyType = _persistentPropertyInfo.PropertyType;
            _allInnerProperties = allInnerProperties;
        }

        public PersistentValueProvider(IPersistentObject persistent, string propertyName, IList<string> allInnerProperties)
        {
            _persistent = persistent;
            _propertyType = persistent.GetType();
            _propertyName = propertyName;
            _allInnerProperties = allInnerProperties;
        }

        public void SetValue(object target, object value)
        {
            throw new NotImplementedException();
        }

        public object GetValue(object target)
        {
            if (target.IsNull())
            {
                return null;
            }

            // was persistent passed or need to be read from target
            var value = _persistent
                ?? _persistentPropertyInfo.GetValue(target, null) as IPersistentObject;

            if (value.IsNull())
            {
                return null;
            }


            var innerProperties = _allInnerProperties
                .Where(p => p.StartsWith(_propertyName + '.'))
                .Select(p => p.Remove(0, _propertyName.Length).TrimStart('.'))
                .ToList();

            var jProperties = new List<JProperty>();

            // the ValueType and String properties
            // (no delimiter "." is used)
            foreach (var propertyName in innerProperties.Where(p => !p.Contains(".")))
            {
                var propertyInfo = _propertyType
                    .GetProperties()
                    .FirstOrDefault(p => p.Name.IsEqual(propertyName));

                if (propertyInfo.IsNull())
                {
                    continue;
                }

                var propertyValue = propertyInfo.GetValue(value, null);

                // expected is ValueType here. Only properties can go this way
                if (propertyValue is IPersistentObject)
                {
                    return null;
                }


                var jProperty = new JProperty(propertyName, propertyValue);
                jProperties.Add(jProperty);
            }

            var innerObjects = innerProperties
                .Where(p => p.Contains("."))
                .Select(p => p.Split('.').First())
                .Distinct()
                .ToList();

            // the inner IPersitent objects
            foreach (var objectName in innerObjects)
            {
                var propertyInfo = _propertyType
                    .GetProperties()
                    .FirstOrDefault(p => p.Name.IsEqual(objectName));

                if (propertyInfo.IsNull())
                {
                    continue;
                }

                var innerProps = innerProperties
                    .Where(p => p.StartsWith(objectName + "."))
                    .ToList();

                var innerObject = new PersistentValueProvider(propertyInfo, innerProps)
                    .GetValue(value);
                //.GetValue(propertyInfo.GetValue(value, null));

                jProperties.Add(new JProperty(objectName, innerObject));
            }

            // skip it unless explitly in the $select
            return jProperties.IsEmpty() ? null : new JObject(jProperties);
        }
    }
}